package org.xteam.box2text.old;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.xteam.box2text.AlignInfo;
import org.xteam.box2text.Frame;
import org.xteam.box2text.Point;
import org.xteam.box2text.Text;
import org.xteam.box2text.TextStruct;


public class AOperator extends Operator implements IPlaceableBox {

	private AlignmentOption[] alignmentOptions;

	public AOperator(Align[] alignments, Box...boxes) {
		super(boxes);
		alignmentOptions = new AlignmentOption[alignments.length];
		for (int i = 0; i < alignmentOptions.length; ++i) {
			alignmentOptions[i] = new AlignmentOption(alignments[i]);
		}
	}
	
	public AOperator(Map<String, Integer> options,
			AlignmentOption[] alignOptions, List<Box> arguments) {
		super(options, arguments);
		this.alignmentOptions = alignOptions;
	}

	public AlignmentOption[] alignOptions() {
		return alignmentOptions;
	}
	
	@Override
	public List<Box> normalizeGroups(boolean splice) {
		return Arrays.asList(new Box[] { new AOperator(options, alignOptions(), normalizeChildren(true)) });
	}
	
	public Box removeVertical(boolean removeRow) {
		return new HOperator(options, removeVertical(arguments, removeRow));
	}

	public int width(Frame frame) {
		return length(frame, OperatorKinds.H).width();
	}
	
	/**
	 * get the column <code>index</code> of the array
	 */
	public List<Box> getColumn(int i) {
		List<Box> results = new ArrayList<Box>();
		for (Box row : arguments) {
			if (row instanceof ROperator) {
				ROperator r = (ROperator) row;
				if (r.arguments.size() <= i)
					results.add(new TextBox(""));
				else
					results.add(r.arguments.get(i));
			} else {
				results.add(row);
			}
		}
		return results;
	}

	public TextStruct place(Frame frame, TextStruct textStruct, int context) {
		AOperator na = new AOperator(options(), alignOptions(), removeVertical(arguments, false));
		AlignInfo alignInfo = calcWidth(na, frame);
		return preAlign(na, frame, textStruct, alignInfo);
	}

	private static TextStruct preAlign(AOperator na, Frame frame, TextStruct textStruct, AlignInfo alignInfo) {
		int Hs = frame.getHs(na.options);
        int availableSpace = frame.getRightMargin() - textStruct.x();
        if (availableSpace >= alignInfo.maximalWidth(Hs)) {
			alignInfo.fixColumnWidthsMaximal();
		} else if (availableSpace < alignInfo.minimalWidth(Hs)) {
        	alignInfo.fixColumnWidthsMinimal();
        } else {
        	alignInfo.fixColumnWidths(Hs, availableSpace);
        }
        Frame f = frame.setLeftMargin(frame.x());
        return align(na, f, textStruct, alignInfo);
	}

	private static TextStruct align(AOperator na, Frame frame, TextStruct textStruct, AlignInfo alignInfo) {
		int Vs = frame.getVs(na.options);
		for (Box box : na.arguments) {
			textStruct = align((ROperator)box, na /* for options */, frame, textStruct, alignInfo);
			frame = frame.vsep(textStruct.point(), Vs);
		}
		return textStruct;
	}
	
	private static TextStruct align(ROperator row, AOperator na, Frame frame,
			TextStruct textStruct, AlignInfo alignInfo) {
		for (int i = 0; i < row.arguments.size(); ++i) {
			AlignmentOption ao = na.alignmentOptions[i];
			int width = alignInfo.getWidth(i);
			Frame f = frame.setPoint(0, 0).setRightMargin(width).setLeftMargin(0);
			TextStruct cellText = ((IPlaceableBox)row.arguments.get(i)).place(f,
					new TextStruct(new Text(), new Point(0, 0)), OperatorKinds.H);
			if (ao.type() == Align.L) {
				cellText = cellText.alignLeft(f);
			} else if (ao.type() == Align.R) {
				cellText = cellText.alignRight(f);
			} else if (ao.type() == Align.C) {
				cellText = cellText.alignCenter(f);
			} else {
				throw new RuntimeException("unknown alignment option");
			}
			textStruct = textStruct.concat(cellText, frame);
			frame = frame.hsep(textStruct.point(), frame.getHs(na.options));
		}
		return textStruct;
	}

	public Point length(Frame frame, int context) {
		Point p = new Point(0,0);
		for (int index = 0; index < alignmentOptions.length; ++index) {
			if (index == 0) {
				IPlaceableBox b = new VOperator(options, getColumn(index));
				Point bp = b.length(frame, OperatorKinds.H);
				int width = b.width(frame);
				p = new Point(width, bp.height());
			} else {
				int width = new VOperator(options, getColumn(index)).width(frame);
				int hs = frame.getHs(options);
				p = new Point(p.x() + hs + width, p.y()); // XXX each column could have different height ??
			}
		}
		return p;
	}

	/**
	 * Calculate the minimal and maximal width of each column.
	 * 
	 * @param a
	 * @param f
	 * @return
	 */
	private static AlignInfo calcWidth(AOperator a, Frame f) {
		AlignInfo info = new AlignInfo();
		for (int i = 0; i < a.alignmentOptions.length; ++i) {
			IPlaceableBox b = new VOperator(a.options, a.getColumn(i));
			int width = b.width(f);
			Point bp = b.length(f, OperatorKinds.H);
			if (bp.width() < width) {
				info.add(new Point(width, width));
			} else {
				info.add(new Point(width, bp.width()));
			}
		}
		return info;
	}

}
